package drds.plus.parser.parser;

import drds.plus.parser.abstract_syntax_tree.expression.Expression;
import drds.plus.parser.abstract_syntax_tree.expression.arithmetic.Add;
import drds.plus.parser.abstract_syntax_tree.expression.arithmetic.Mod;
import drds.plus.parser.abstract_syntax_tree.expression.arithmetic.Subtract;
import drds.plus.parser.abstract_syntax_tree.expression.comparison.Is;
import drds.plus.parser.abstract_syntax_tree.expression.comparison.fuzzy_matching.Like;
import drds.plus.parser.abstract_syntax_tree.expression.comparison.fuzzy_matching.Regexp;
import drds.plus.parser.abstract_syntax_tree.expression.comparison.range.*;
import drds.plus.parser.abstract_syntax_tree.expression.logical.And;
import drds.plus.parser.abstract_syntax_tree.expression.logical.Or;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.Function;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.FunctionManager;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.aggregation.*;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.datetime.CurrentDate;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.datetime.CurrentTime;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.datetime.Now;
import drds.plus.parser.abstract_syntax_tree.expression.primary.literal.LiteralBoolean;
import drds.plus.parser.abstract_syntax_tree.expression.primary.literal.LiteralNull;
import drds.plus.parser.abstract_syntax_tree.expression.primary.literal.LiteralNumber;
import drds.plus.parser.abstract_syntax_tree.expression.primary.literal.LiteralString;
import drds.plus.parser.abstract_syntax_tree.expression.primary.misc.*;
import drds.plus.parser.abstract_syntax_tree.expression.primary.subquery.AllQuery;
import drds.plus.parser.abstract_syntax_tree.expression.primary.subquery.AnyQuery;
import drds.plus.parser.abstract_syntax_tree.expression.primary.subquery.Exists;
import drds.plus.parser.abstract_syntax_tree.statement.Query;
import drds.plus.parser.lexer.Lexer;
import drds.plus.parser.lexer.Token;

import java.util.LinkedList;
import java.util.List;

import static drds.plus.parser.lexer.Token.*;

/**
 * 约定每个方法都调用了lexer.nextToken();
 */
public class ExpressionParser extends Parser {
    private final FunctionManager functionManager;
    private SelectParser selectParser;

    public ExpressionParser(Lexer lexer) {
        this(lexer, FunctionManager.FUNCTION_MANAGER, true);
    }

    public ExpressionParser(Lexer lexer, FunctionManager functionManager, boolean cacheEvalRst) {
        super(lexer, cacheEvalRst);
        this.functionManager = functionManager;
    }

    public void setSelectParser(SelectParser selectParser) {
        this.selectParser = selectParser;
    }

    public Expression expression() {
        Token token = lexer.token();
        //入口函数补齐token
        if (lexer.token() == null) {
            lexer.nextToken();
        }
        if (token == end_of_sql) {
            error("unexpected end_of_sql");
        }
        return or();
    }

    private Expression or() {
        Or or = null;
        Expression expression = null;
        while (true) {
            Expression and = and();
            switch (lexer.token()) {
                case KW_OR: {
                    if (or == null) {
                        or = new Or();
                        or.setCacheEvalValue(cacheEvalRst);
                        expression = or;
                    }
                    or.append(and);
                    lexer.nextToken();
                    break;
                }
                default:
                    if (expression != null) {
                        return expression;//or
                    } else {
                        return and;
                    }
            }
        }
    }

    private Expression and() {
        And and = null;
        Expression expression = null;
        while (true) {
            Expression andItem = expressionItem();
            switch (lexer.token()) {
                case KW_AND:
                    if (and == null) {
                        and = new And();
                        and.setCacheEvalValue(cacheEvalRst);
                        expression = and;
                    }
                    and.append(andItem);
                    lexer.nextToken();
                    break;
                default:
                    if (expression != null) {
                        ((And) expression).append(andItem);
                        return expression;//and
                    } else {
                        return andItem;
                    }
            }
        }
    }

    private Expression expressionItem() {
        /**
         * 只做列名处理
         */
        Expression expression = primaryExpression();
        switch (lexer.token()) {
            case KW_NOT:
                lexer.nextToken();
                switch (lexer.token()) {
                    case KW_LIKE: {
                        lexer.nextToken();
                        Expression text = primaryExpression();
                        Expression escape = null;
                        if (stringValueIndexOf("ESCAPE") >= 0) {
                            lexer.nextToken();
                            escape = primaryExpression();
                        }
                        return new Like(true, expression, text, escape).setCacheEvalValue(cacheEvalRst);
                    }
                    case KW_RLIKE:
                    case KW_REGEXP: {
                        lexer.nextToken();
                        Expression text = primaryExpression();
                        return new Regexp(true, expression, text).setCacheEvalValue(cacheEvalRst);
                    }
                    case KW_BETWEEN:
                        lexer.nextToken();
                        Expression notLessThan = primaryExpression();
                        match(KW_AND);
                        Expression notGreaterThan = primaryExpression();
                        return new BetweenAnd(true, expression, notLessThan, notGreaterThan).setCacheEvalValue(cacheEvalRst);
                    case KW_IN:
                        lexer.nextToken();
                        if (lexer.token() != PUNC_LEFT_PAREN) {
                            return expression;
                        }
                        Expression in = in();
                        return new In(true, expression, in).setCacheEvalValue(cacheEvalRst);
                    default:
                        throw error("unexpect token after NOT: " + lexer.token());
                }
            case KW_LIKE: {
                lexer.nextToken();
                Expression text = primaryExpression();
                Expression escape = null;
                if (stringValueIndexOf("ESCAPE") >= 0) {
                    lexer.nextToken();
                    escape = primaryExpression();
                }
                return new Like(false, expression, text, escape).setCacheEvalValue(cacheEvalRst);
            }
            case KW_RLIKE:
            case KW_REGEXP: {
                lexer.nextToken();
                Expression text = primaryExpression();
                return new Regexp(false, expression, text).setCacheEvalValue(cacheEvalRst);
            }
            case KW_BETWEEN:
                lexer.nextToken();
                Expression notLessThan = primaryExpression();
                match(KW_AND);
                Expression notGreaterThan = primaryExpression();
                return new BetweenAnd(false, expression, notLessThan, notGreaterThan).setCacheEvalValue(cacheEvalRst);
            case KW_IN: {
                if (lexer.nextToken() != PUNC_LEFT_PAREN) {
                    return expression;
                }
                Expression in = in();
                return new In(false, expression, in).setCacheEvalValue(cacheEvalRst);
            }
            case KW_IS:
                switch (lexer.nextToken()) {
                    case KW_NOT:
                        switch (lexer.nextToken()) {
                            case LITERAL_NULL:
                                lexer.nextToken();
                                return new Is(expression, Is.IS_NOT_NULL).setCacheEvalValue(cacheEvalRst);
                            case LITERAL_BOOL_TRUE:
                                lexer.nextToken();
                                return new Is(expression, Is.IS_NOT_TRUE).setCacheEvalValue(cacheEvalRst);
                            case LITERAL_BOOL_FALSE:
                                lexer.nextToken();
                                return new Is(expression, Is.IS_NOT_FALSE).setCacheEvalValue(cacheEvalRst);
                            default:
                                match("UNKNOWN");
                                return null;
                        }
                    case LITERAL_NULL:
                        lexer.nextToken();
                        return new Is(expression, Is.IS_NULL).setCacheEvalValue(cacheEvalRst);
                    case LITERAL_BOOL_TRUE:
                        lexer.nextToken();
                        return new Is(expression, Is.IS_TRUE).setCacheEvalValue(cacheEvalRst);
                    case LITERAL_BOOL_FALSE:
                        lexer.nextToken();
                        return new Is(expression, Is.IS_FALSE).setCacheEvalValue(cacheEvalRst);
                    default:
                        match("UNKNOWN");
                        return null;
                }//end is

            case OP_GREATER_THAN: {
                lexer.nextToken();
                Expression value = primaryExpressionWithSubQuery();
                return new GreaterThan(expression, value).setCacheEvalValue(cacheEvalRst);
            }
            case OP_GREATER_OR_EQUALS: {
                lexer.nextToken();
                Expression value = primaryExpressionWithSubQuery();
                return new GreaterThanOrEquals(expression, value).setCacheEvalValue(cacheEvalRst);
            }
            case OP_EQUALS: {
                lexer.nextToken();
                Expression value = primaryExpressionWithMathematicalCalculation();//primaryExpression,只有在等于的情况下才能进行+-*/
                return new Equal(expression, value).setCacheEvalValue(cacheEvalRst);
            }
            case OP_NOT_EQUALS: {
                lexer.nextToken();
                Expression value = primaryExpression();
                return new NotEqual(expression, value).setCacheEvalValue(cacheEvalRst);
            }
            case OP_LESS_THAN: {
                lexer.nextToken();
                Expression value = primaryExpressionWithSubQuery();
                return new LessThan(expression, value).setCacheEvalValue(cacheEvalRst);
            }
            case OP_LESS_THAN_OR_EQUALS: {
                lexer.nextToken();
                Expression value = primaryExpressionWithSubQuery();
                return new LessThanOrEquals(expression, value).setCacheEvalValue(cacheEvalRst);
            }
            default:
                /**
                 * 下面这几项一般不放到a=b的a部分,如果存在则给外部使用
                 */
                if (expression instanceof LiteralString ||//
                        expression instanceof LiteralNumber ||//
                        expression instanceof ParameterMarker ||//
                        //
                        expression instanceof Identifier ||//
                        expression instanceof Wildcard ||//
                        expression instanceof Exists ||//单值表达式
                        //
                        expression instanceof Function
                ) {
                    return expression;
                }
                throw new IllegalArgumentException("未知表达式:" + expression);
        }
    }


    /**
     * 最主要的表达式单元.里面的每个表达式执行后都会调用lexer.nextToken();使用$$来表示调用了lexer.nextToken();
     */
    private Expression primaryExpression() {
        switch (lexer.token()) {
            case LITERAL_NULL:
                lexer.nextToken();
                return new LiteralNull().setCacheEvalValue(cacheEvalRst);
            case LITERAL_BOOL_TRUE:
                lexer.nextToken();
                return new LiteralBoolean(true).setCacheEvalValue(cacheEvalRst);
            case LITERAL_BOOL_FALSE:
                lexer.nextToken();
                return new LiteralBoolean(false).setCacheEvalValue(cacheEvalRst);
            //
            case LITERAL_CHARS: {
                String value = lexer.stringValue();
                lexer.nextToken();
                return new LiteralString(value).setCacheEvalValue(cacheEvalRst);
            }
            case LITERAL_NUM_INTEGER: {
                Number number = lexer.integerValue();
                lexer.nextToken();
                return new LiteralNumber(number).setCacheEvalValue(cacheEvalRst);
            }
            case LITERAL_NUM_DECIMAL: {
                Number number = lexer.decimalValue();
                lexer.nextToken();
                return new LiteralNumber(number).setCacheEvalValue(cacheEvalRst);
            }
            case QUESTION_MARK:
                int index = lexer.parameterIndex();
                lexer.nextToken();
                return createParameterMarker(index);
            case OP_ASTERISK:
                lexer.nextToken();
                return new Wildcard(null).setCacheEvalValue(cacheEvalRst);
            //
            case KW_EXISTS: {
                lexer.nextToken();
                match(PUNC_LEFT_PAREN);
                lexer.nextToken();
                Expression expression = subQuery();
                match(PUNC_RIGHT_PAREN);
                lexer.nextToken();
                return new Exists((Query) expression).setCacheEvalValue(cacheEvalRst);
            }
            //
            case KW_NOW:
                lexer.nextToken();
                match(PUNC_LEFT_PAREN);
                lexer.nextToken();
                match(PUNC_RIGHT_PAREN);
                lexer.nextToken();
                return new Now().setCacheEvalValue(cacheEvalRst);
            case KW_CURRENT_DATE:
                lexer.nextToken();
                match(PUNC_LEFT_PAREN);
                lexer.nextToken();
                match(PUNC_RIGHT_PAREN);
                lexer.nextToken();
                return new CurrentDate().setCacheEvalValue(cacheEvalRst);
            case KW_CURRENT_TIME:
                lexer.nextToken();
                match(PUNC_LEFT_PAREN);
                lexer.nextToken();
                match(PUNC_RIGHT_PAREN);
                lexer.nextToken();
                return new CurrentTime().setCacheEvalValue(cacheEvalRst);
            //
            case IDENTIFIER:
                String stringValue = lexer.stringValue();
                lexer.nextToken();
                //只有是. (才往后面走
                if (lexer.token() != PUNC_DOT && lexer.token() != PUNC_LEFT_PAREN) {
                    return new Identifier(null, stringValue);
                } else {
                    return startFromIdentifier(stringValue);
                }
            default:
                throw error("未知token: " + lexer.token());
        }
    }


    /**
     * lexer.token() 需要和这个保持一致 {@link ExpressionParser#primaryExpression()}
     */
    private Expression startFromIdentifier(final String consumedStringValue) {
        switch (lexer.token()) {
            case PUNC_DOT: {
                Expression expression = new Identifier(null, consumedStringValue).setCacheEvalValue(cacheEvalRst);
                lexer.nextToken();
                switch (lexer.token()) {
                    case IDENTIFIER:
                        expression = new Identifier((Identifier) expression, lexer.stringValue()).setCacheEvalValue(cacheEvalRst);
                        lexer.nextToken();
                        break;
                    case OP_ASTERISK:
                        lexer.nextToken();
                        return new Wildcard((Identifier) expression).setCacheEvalValue(cacheEvalRst);
                    default:
                        throw error("expect IDENTIFIER or '*' after '.', but is " + lexer.token());
                }
                return expression;
            }
            case PUNC_LEFT_PAREN:
                String functionName = consumedStringValue;
                switch (functionManager.getFunctionParsingStrategy(functionName)) {
                    case ROW: {
                        lexer.nextToken();
                        List<Expression> expressionList = expressionList(new LinkedList<Expression>());
                        lexer.nextToken();
                        return new RowValues(expressionList).setCacheEvalValue(cacheEvalRst);
                    }
                    case MAX: {
                        boolean groupDistinct;
                        if (lexer.nextToken() == KW_DISTINCT) {
                            groupDistinct = true;
                            lexer.nextToken();
                        } else {
                            groupDistinct = false;
                        }
                        Expression expression = expression();
                        match(PUNC_RIGHT_PAREN);
                        lexer.nextToken();
                        return new Max(expression, groupDistinct).setCacheEvalValue(cacheEvalRst);
                    }
                    case MIN: {
                        boolean groupDistinct;
                        if (lexer.nextToken() == KW_DISTINCT) {
                            groupDistinct = true;
                            lexer.nextToken();
                        } else {
                            groupDistinct = false;
                        }
                        Expression expression = expression();
                        match(PUNC_RIGHT_PAREN);
                        lexer.nextToken();
                        return new Min(expression, groupDistinct).setCacheEvalValue(cacheEvalRst);
                    }
                    case SUM: {
                        boolean groupDistinct;
                        if (lexer.nextToken() == KW_DISTINCT) {
                            groupDistinct = true;
                            lexer.nextToken();
                        } else {
                            groupDistinct = false;
                        }
                        Expression expression = expression();
                        match(PUNC_RIGHT_PAREN);
                        lexer.nextToken();
                        return new Sum(expression, groupDistinct).setCacheEvalValue(cacheEvalRst);
                    }
                    case COUNT: {
                        if (lexer.nextToken() == KW_DISTINCT) {
                            lexer.nextToken();
                            List<Expression> expressionList = expressionList(new LinkedList<Expression>());
                            return new Count(expressionList).setCacheEvalValue(cacheEvalRst);
                        }
                        Expression expression = expression();
                        match(PUNC_RIGHT_PAREN);
                        lexer.nextToken();
                        return new Count(expression).setCacheEvalValue(cacheEvalRst);
                    }
                    case AVG: {
                        boolean groupDistinct;
                        if (lexer.nextToken() == KW_DISTINCT) {
                            groupDistinct = true;
                            lexer.nextToken();
                        } else {
                            groupDistinct = false;
                        }
                        Expression expression = expression();
                        match(PUNC_RIGHT_PAREN);
                        lexer.nextToken();
                        return new Avg(expression, groupDistinct).setCacheEvalValue(cacheEvalRst);
                    }
                    default:
                        throw error("unexpected function parsing strategy for id of " + consumedStringValue);
                }
            default:
                return new Identifier(null, consumedStringValue).setCacheEvalValue(cacheEvalRst);
        }
    }

    /**
     * 如果包含了+ - * div %这几个就返回相应的二元表达式,否则只返回一元值
     */
    private Expression primaryExpressionWithMathematicalCalculation() {
        Expression left = primaryExpression();
        switch (lexer.token()) {

            case OP_PLUS: {
                lexer.nextToken();
                Expression right = primaryExpression();
                return new Add(left, right).setCacheEvalValue(cacheEvalRst);
            }
            case OP_MINUS: {
                lexer.nextToken();
                Expression right = primaryExpression();
                return new Subtract(left, right).setCacheEvalValue(cacheEvalRst);
            }
            case KW_MOD: {
                lexer.nextToken();
                Expression right = primaryExpression();
                return new Mod(left, right).setCacheEvalValue(cacheEvalRst);
            }
            default:
                return left;
        }
    }

    /**
     * (非关联子查询 数字  ?)
     */
    private Expression primaryExpressionWithSubQuery() {
        switch (lexer.token()) {
            case KW_ALL: {
                lexer.nextToken();
                match(PUNC_LEFT_PAREN);
                lexer.nextToken();
                Query subQuery = subQuery();
                match(PUNC_RIGHT_PAREN);
                lexer.nextToken();
                return new AllQuery(subQuery).setCacheEvalValue(cacheEvalRst);
            }
            case KW_ANY: {
                lexer.nextToken();
                match(PUNC_LEFT_PAREN);
                lexer.nextToken();
                Query subQuery = subQuery();
                match(PUNC_RIGHT_PAREN);
                lexer.nextToken();
                return new AnyQuery(subQuery).setCacheEvalValue(cacheEvalRst);
            }
            default:
                return primaryExpression();
        }
    }


    private Expression in() {
        match(PUNC_LEFT_PAREN);
        lexer.nextToken();
        if (lexer.token() == KW_SELECT) {
            Query subQuery = subQuery();
            match(PUNC_RIGHT_PAREN);
            lexer.nextToken();
            return subQuery;
        } else {
            Expression inList = new InList(expressionList(new LinkedList<Expression>())).setCacheEvalValue(cacheEvalRst);
            lexer.nextToken();
            return inList;
        }
    }

    /**
     * 进行(之后的处理,遇到的第一个标识是select
     */
    private Query subQuery() {
        if (selectParser == null) {
            selectParser = new SelectParser(lexer, this);
        }
        Query query = selectParser.selectStatement();
        return query;
    }

    /**
     * 这个方法之前已经进行类(的处理,调用了lexer.nextToken();,现在是(下一个标识的处理
     */
    private List<Expression> expressionList(List<Expression> expressionList) {
        for (; ; ) {
            Expression expression = expression();
            expressionList.add(expression);//已经调用了lexer.nextToken();
            switch (lexer.token()) {
                case PUNC_COMMA:
                    lexer.nextToken();
                    break;
                case PUNC_RIGHT_PAREN:
                    lexer.nextToken();
                    return expressionList;
                default:
                    throw error("unexpected token: " + lexer.token());
            }
        }
    }
}
